package com.spring.security;

import com.spring.security.authentication.AbstractChannelSecurityConfig;
import com.spring.security.authentication.mobile.SmsCodeAuthenticationSecurityConfig;
import com.spring.security.properties.SecurityConstants;
import com.spring.security.properties.SecurityProperties;
import com.spring.security.validate.code.ValidateCodeSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.social.security.SpringSocialConfigurer;

import javax.sql.DataSource;

@Configuration
public class BrowserSecurityConfig extends AbstractChannelSecurityConfig{

    @Autowired
    private SecurityProperties securityProperties;


    // 数据源是需要在使用处配置数据源的信息
    @Autowired
    private DataSource dataSource;

    // 之前已经写好的 MyUserDetailsService
    @Autowired
    private UserDetailsService userDetailsService;

    //SmsCodeAuthenticationSecurityConfig加入到认证流程
    @Autowired
    private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;

    @Autowired
    private ValidateCodeSecurityConfig validateCodeSecurityConfig;

    @Autowired
    private SpringSocialConfigurer hkSocialSecurityConfig;

    @Autowired
    private SessionInformationExpiredStrategy sessionInformationExpiredStrategy;

    @Autowired
    private InvalidSessionStrategy invalidSessionStrategy;

    @Autowired
    private LogoutSuccessHandler logoutSuccessHandler;



    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        // org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer.tokenRepository
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        // 该对象里面有定义创建表的语句
        //create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null)
        // 可以设置让该类来创建表
        // 但是该功能只用使用一次，如果数据库已经存在表则会报错
        //jdbcTokenRepository.setCreateTableOnStartup(true);
        //两种方法二选一
        return jdbcTokenRepository;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //登录相关提出去
        applyPasswordAuthenticationConfig(http);
        //配置
        http.apply(validateCodeSecurityConfig)
                .and()
                //加入短信验证码认证流程
                .apply(smsCodeAuthenticationSecurityConfig)
                .and()
                //加入社交登录
                .apply(hkSocialSecurityConfig)
                .and()
                // 从这里开始配置记住我的功能
                .rememberMe()
                .tokenRepository(persistentTokenRepository())
                // 新增过期配置，单位秒
                .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
                // userDetailsService 是必须的。不然就报错
                .userDetailsService(userDetailsService)
                .and()
                //session失效跳转的路径
                .sessionManagement()
                .invalidSessionStrategy(invalidSessionStrategy)
                //最多登陆个数
                .maximumSessions(securityProperties.getBrowser().getSession().getMaximumSessions())
                //达到最大session时是否阻止新的登录请求，默认为false，不阻止，新的登录会将老的登录失效掉
                .maxSessionsPreventsLogin(securityProperties.getBrowser().getSession().isMaxSessionsPreventsLogin())
                //达到最大session时跳转请求
                .expiredSessionStrategy(sessionInformationExpiredStrategy)
                .and()
                .and()
                .logout().logoutUrl("/signOut")
                //退出处理器
                .logoutSuccessHandler(logoutSuccessHandler)
                //退出时删除cookies
                .deleteCookies("JSESSIONID")
                .and()
                .authorizeRequests() // 授权配置
                //不需要认证的路径
                .antMatchers(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
                        SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,
                        SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*",
                        securityProperties.getBrowser().getLoginPage(),
                        "/failure",
                        securityProperties.getBrowser().getSignUpUrl(),
                        "/user/regist",
                        SecurityConstants.DEFAULT_SESSION_INVALID_URL,
                        securityProperties.getBrowser().getSignOutUrl()).permitAll()
                .anyRequest() // 所有请求
                .authenticated() // 都需要认证
                .and().csrf().disable();
    }
}
